﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using GameCreator.Core;
using MoreMountains.Tools;
using QFramework;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

namespace Gp.Scripts.Core.Controller {
    public class SceneLoadUIController : MonoBehaviour, ICanSendEvent {
        public Text loadingText;

        /// the canvas group containing the progress bar
        public CanvasGroup loadingProgressBar;

        /// the canvas group containing the animation
        public CanvasGroup loadingAnimation;

        public CanvasGroup loadingCompleteAnimation;

        [SerializeField] private GameObject window;
        
        

        [Header("Time")]
        /// the duration (in seconds) of the initial fade in
        public float startFadeDuration = 0.2f;

        /// the speed of the progress bar
        public float progressBarSpeed = 2f;

        /// the duration (in seconds) of the load complete fade out
        public float exitFadeDuration = 0.2f;

        /// the delay (in seconds) before leaving the scene when complete
        public float loadCompleteDelay = 0.5f;

        // private AsyncOperation _asyncOperation;
        private float _fadeDuration = 0.5f;
        private float _fillTarget = 0f;
        private string _loadingTextValue;
        private Image _progressBarImage;

        private static MMTweenType _tween;


        private void Start() {
            _tween            = new MMTweenType(MMTween.MMTweenCurve.EaseOutCubic);
            _progressBarImage = loadingProgressBar.GetComponent<Image>();
            _loadingTextValue = loadingText.text;
            CoroutinesManager.Instance.StartCoroutine(SceneLoadCoroutine());
        }



        /// <summary>
        /// Every frame, we fill the bar smoothly according to loading progress
        /// </summary>
        protected virtual void Update() {
            Time.timeScale = 1f;
            _progressBarImage.fillAmount = MMMaths.Approach(_progressBarImage.fillAmount, _fillTarget,
                Time.deltaTime * progressBarSpeed);
        }


        /// <summary>
        /// Loads the scene to load asynchronously.
        /// </summary>
        protected virtual IEnumerator SceneLoadCoroutine() {
            // we setup our various visual elements
            LoadingSetup();
            this.SendEvent(new SceneLoadEvent(LoadingStatus.BeforeStart));            
            yield return new WaitForSeconds(startFadeDuration);


            var scenes = SceneManageSystem.PopLoadingSceneArgs();
            if (scenes == null) {
                this.SendEvent(new SceneLoadEvent(LoadingStatus.AfterComplete));
                yield break;
            }


            List<AsyncOperation> loadOperations = new(scenes.Length);
            List<AsyncOperation> unloadOperations = new(SceneManager.sceneCount);

            
            for (int i = 0; i < SceneManager.sceneCount; i++) {
                var scene = SceneManager.GetSceneAt(i);
                if (scene.IsPersistentScene() || scene.IsLoadingScene()) {
                    continue;
                }

                unloadOperations.Add(SceneManager.UnloadSceneAsync(scene));
            }


            loadOperations.AddRange(scenes.Select(s => SceneManager.LoadSceneAsync(s, LoadSceneMode.Additive)));
            loadOperations.ForEach(x => x.allowSceneActivation = false);

            int len = loadOperations.Count + unloadOperations.Count;
            float progress = 0;

            // while the scene loads, we assign its progress to a target that we'll use to fill the progress bar smoothly
            while (progress < 0.899f) {
                progress = loadOperations.Sum(x => x.progress) 
                           + unloadOperations.Sum(x => x.progress);
                progress /= len;
                
                _fillTarget = progress;
                yield return null;
            }

            // when the load is close to the end (it'll never reach it), we set it to 100%
            _fillTarget = 1f;

            // we wait for the bar to be visually filled to continue
            while (Math.Abs(_progressBarImage.fillAmount - _fillTarget) > .001f) {
                yield return null;
            }

            // the load is now complete, we replace the bar with the complete animation
            LoadingComplete();
            yield return new WaitForSeconds(loadCompleteDelay);


           

            // we switch to the new scene
            loadOperations.ForEach(x => x.allowSceneActivation = true);


            while (loadOperations.Any(x => !x.isDone)) {
                yield return null;
            }
            
            // we fade to black
            // MMFadeInEvent.Trigger(exitFadeDuration, _tween);
            SceneManageSystem.AfterSceneLoadTrigger();
            this.SendEvent(new SceneLoadEvent(LoadingStatus.AfterComplete));


            // Global.Get<GUIManager>().BlackCover.DoQuickFade(0.05f, .15f, .1f);
            yield return new WaitForSeconds(.2f);
            
            SceneManager.UnloadSceneAsync(gameObject.scene);
        }


        /// <summary>
        /// Sets up all visual elements, fades from black at the start
        /// </summary>
        protected virtual void LoadingSetup() {
            loadingCompleteAnimation.alpha = 0;
            _progressBarImage.fillAmount = 0f;
            loadingText.text = _loadingTextValue;
            // window.Show();
        }


        /// <summary>
        /// Triggered when the actual loading is done, replaces the progress bar with the complete animation
        /// </summary>
        protected virtual void LoadingComplete() {
            loadingCompleteAnimation.gameObject.SetActive(true);
            StartCoroutine(MMFade.FadeCanvasGroup(loadingProgressBar, 0.1f, 0f));
            StartCoroutine(MMFade.FadeCanvasGroup(loadingAnimation, 0.1f, 0f));
            StartCoroutine(MMFade.FadeCanvasGroup(loadingCompleteAnimation, 0.1f, 1f));
        }


        public IArchitecture GetArchitecture() {
            return MainArchitecture.Interface;
        }
    }
}